package 磁盘调度;

import java.io.*;
import java.util.Arrays;
import java.util.Scanner;

public class Demo {
    public static void main(String[] args) throws Exception{
        //从文件读入磁道号
        File file = new File("../data.txt");
        BufferedReader br = new BufferedReader(new FileReader(file));
        String firstLine = br.readLine();//读取第一行
        String secondLine=br.readLine();//读取第二行
        String[] arrs = firstLine.split(" ");
        int m=0;
        //将字符数组转为int数组
        int[] cidao = new int[arrs.length];
        for (int i = 0; i < arrs.length; i++) {
            cidao[i] = Integer.parseInt(arrs[i]);
        }
        m=Integer.parseInt(secondLine);//字符串转换为数字
        br.close();
//        for (int i = 0; i < arrs.length; i++) {
//            System.out.print(cidao[i]+" ");
//        }
//        System.out.println();
//        System.out.println(m);
        Demo A = new Demo();//创建出来一个对象，然后存储在d中，可以重复使用。
        int choice=-1;
        while (true) {
            System.out.println("-----------------------------------------------");
            System.out.println("|                 磁盘调度算法菜单                |");
            System.out.println("|                                              |");
            System.out.println("|               1. 先来先服务                    |");
            System.out.println("|               2. 最短寻道时间优先               |");
            System.out.println("|               3. 扫描（电梯）调度               |");
            System.out.println("|               4. 循环扫描                     |");
            System.out.println("|               5. 退出                        |");
            System.out.println("----------------------------------------------");
            System.out.print("请选择算法（请输入1-5之间的数字）：");
            Scanner input = new Scanner(System.in);
            choice = input.nextInt();
            if (choice == 5) {
                System.out.println("已成功提出磁盘调度算法系统！");
                break;
            }
            switch (choice) {
                case 1:
                    A.FCFS(cidao,m);
                    break;
                case 2:
                    A.SSTF(cidao,m);
                    break;
                case 3:
                    A.SCAN(cidao,m);
                    break;
                case 4:
                    A.CSCAN(cidao,m);
                    break;
                default:
                    System.out.println("请输入1-5之间的数字");
            }
        }
    }
    //先来先服务算法
    void FCFS(int cidao[], int m) {
        int now = -1;//当前磁道号
        int sum = 0;//总寻道长度
        int i;
        float ave = 0.0f;//平均寻道长度
        System.out.println("磁盘请求序列为：");
        for (i = 0; i < m; i++) {
            System.out.print(cidao[i] + " ");
        }
        System.out.println();
        System.out.print("请输入当前磁道号：");
        Scanner input = new Scanner(System.in);
        now = input.nextInt();
        sum += Math.abs(cidao[0] - now);//求当前输入磁道和第一次访问磁道的距离
        System.out.println("磁盘扫描序列为：");
        System.out.print(now);
        for (i = 0; i < m; i++) {
            System.out.print("-->" + cidao[i]);
        }
        for (i = 0; i < m - 1; i++) {//求平均寻道长度
            sum += Math.abs(cidao[i + 1] - cidao[i]);
            ave = (float) sum / m;
        }
        System.out.println();
        System.out.println("移动总量为：" + sum);
        System.out.println("平均寻道长度为：" + ave);
    }

    //最短寻道时间优先算法
    void SSTF(int cidao[], int m) {
        int k = 1; //当前磁道号大于请求序列中最小者且小于最大者，找到当前磁道号在排好序列的位置
        int now = -1, left = -1, right = -1;
        int i, j, sum = 0;
        float ave = 0.0f;//用来存平均寻道长度
        System.out.println("磁盘请求序列为：");
        for (i = 0; i < m; i++) {
            System.out.print(cidao[i] + " ");
        }
        System.out.println();
        Arrays.sort(cidao);//快速排序升序处理
        System.out.println("排好序的磁道号序列为：");
        for (int n = 0; n < cidao.length; n++) {
            System.out.print(cidao[n] + " ");
        }
        System.out.println();
        System.out.print("请输入当前的磁道号：");
        Scanner input = new Scanner(System.in);
        now = input.nextInt();
        //情况一：若当前磁道号大于请求序列中最大者，则直接由内向外依次给予各请求服务
        if (cidao[m - 1] <= now) {
            System.out.println("磁盘扫描序列为：");
            for (i = 0; i < m; i++) {
                System.out.print(cidao[i] + "-->");
            }
            System.out.println(now);
            sum = now - cidao[0];
        }
        //情况二：当前磁道号小于请求序列中最小者，则直接由外向内依次给予各请求服务，与第一种同理
        if (cidao[0] >= now) {
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            for (i = 0; i < m; i++)
                System.out.print(" --> " + cidao[i]);
            sum = cidao[m - 1] - now;
        }
        //情况三：若当前磁道号大于请求序列中最小者且小于最大者
        if (now > cidao[0] && now < cidao[m - 1]) {
            System.out.println("磁盘扫描序列为：");
            while (cidao[k] < now) {//确定当前磁道位置
                k++;
            }
            left = k - 1;//此时位置的左边逻辑下标
            right = k;//此时位置的右边逻辑下标
            System.out.print(now);
            while ((left >= 0) && (right < m)) {//当前磁道在请求序列范围内
                if ((now - cidao[left]) <= (cidao[right] - now)) {
                    System.out.print("-->" + cidao[left]);//左边距离近的情况
                    sum += now - cidao[left];
                    now = cidao[left];

                    left = left - 1;
                } else {
                    System.out.print(" --> " + cidao[right]);  //这是右边距离近的情况
                    sum += cidao[right] - now;
                    now = cidao[right];
                    right = right + 1;
                }
            }
            if (left == -1) {//磁头移动到序列的最小号，返回内侧扫描未扫描的磁道
                for (j = right; j < m; j++) {
                    System.out.print("-->" + cidao[j]);
                }
                sum += cidao[m - 1] - cidao[0];
            } else {//磁头移动到序列的最大号，返回外侧扫描仍未扫描的磁道
                for (j = left; j >= 0; j--) {
                    System.out.print("--> " + cidao[j]);
                }
                sum += cidao[m - 1] - cidao[0];
            }
        }
        ave = sum / (float) (m);
        System.out.println();
        System.out.println("移动总量为：" + sum);
        System.out.println("平均寻道长度为：" + ave);
    }

    //扫描算法(电梯)
    void SCAN(int cidao[], int m) {
        int k = 1;
        int now, left = -1, right = -1, choice = -1;
        int i, j, sum = 0;
        float ave = 0.0f;
        System.out.println("磁盘请求序列为：");
        for (i = 0; i < m; i++) {
            System.out.print(cidao[i] + " ");
        }
        System.out.println();
        Arrays.sort(cidao);
        System.out.println("排好序的磁道号序列为：");
        for (int n = 0; n < cidao.length; n++) {
            System.out.print(cidao[n] + " ");
        }
        System.out.println();
        System.out.print("请输入当前的磁道号：");
        Scanner input = new Scanner(System.in);
        now = input.nextInt();
        //情况一：若当前磁道号大于请求序列中最大者，则直接由内向外依次给予各请求服务
        if (cidao[m - 1] <= now) {
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            for (i = m - 1; i >= 0; i--) {
                System.out.print("-->" + cidao[i]);
            }
            sum = now - cidao[0];//反正都要扫描到最外侧，直接减最小的那个得的距离和一个一个减在相加是等价的
        }
        //情况二：若当前磁道号小于请求序列中最小者，则直接由外向内依次给予各请求服务
        if (cidao[0] >= now) {
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            for (i = 0; i < m; i++)
                System.out.print(" --> " + cidao[i]);
            sum = cidao[m - 1] - now;        //同理
        }
        //情况三：若当前磁道号大于请求序列中最小者且小于最大者
        if (now > cidao[0] && now < cidao[m - 1]) {
            while (cidao[k] < now) {//先找到当前磁道号的位置
                k++;
            }
            left = k - 1;
            right = k;
            System.out.println("请输入当前磁道移动的方向（1表示内向，0表示外向）：");
            Scanner input2 = new Scanner(System.in);
            choice = input2.nextInt();
            if (choice == 0) {//先向外扫描
                System.out.println("（向外）磁盘扫描序列为：");
                System.out.print(now);
                for (j = left; j >= 0; j--) {//磁道号小的方向移动
                    System.out.print("-->" + cidao[j]);
                }
                for (j = right; j < m; j++) {//磁头移动到最小号，则改变方向向内扫描未扫描的磁道
                    System.out.print("-->" + cidao[j]);
                }
                sum = now - 2 * cidao[0] + cidao[m - 1];
            } else {//选择移动方向向外，则先向内扫描
                System.out.println("磁盘扫描序列为：");
                System.out.print(now);
                for (j = right; j < m; j++) {
                    System.out.print(" -->" + cidao[j]);
                }
                for (j = left; j >= 0; j--)    //磁头移动到最大号，则改变方向向外扫描未扫描的磁道
                {
                    System.out.print(" -->" + cidao[j]);
                }
                sum=now-cidao[0]+cidao[m-1]-cidao[0];//同理sum =(cidao[m-1]-now)*2 + (now - cidao[0])
              //sum = -now - cidao[0] + 2 * cidao[m - 1];
            }
        }
        ave = sum / (float) (m);
        System.out.println();
        System.out.println("移动总量为：" + sum);
        System.out.println("平均寻道长度为：" + ave);
    }

    //循环扫描算法
    void CSCAN(int cidao[], int m) {//默认的扫描方向是磁道号变小的方向进行，即从内到外
        int k=1;
        int now=-1,left=-1,right=-1;
        int i,j,sum=0;
        float ave=0.0f;
        System.out.println("磁盘请求序列为：");
        for (i = 0; i < m; i++) {
            System.out.print(cidao[i] + " ");
        }
        System.out.println();
        Arrays.sort(cidao);
        System.out.println("排好序的磁道号序列为：");
        for(int n=0;n< cidao.length;n++){
            System.out.print(cidao[n]+" ");
        }
        System.out.println();
        System.out.println("请输入当前的磁道号：");
        Scanner input=new Scanner(System.in);
        now=input.nextInt();
        //情况一：若当前磁道号大于请求序列中最大者，则直接将移动臂移动到最小号磁道依次向内给予各请求服务
        if(cidao[m-1]<=now){
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            for(i=m-1;i>=0;i--){
                System.out.print("-->"+cidao[i]);
            }
            sum=now-cidao[0];
        }
        //情况二：若当前磁道号小于请求序列中最小者，则移动磁道到最大位置由外向内依次给予各请求服务
        if(cidao[0]>=now){
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            for(i=m-1;i>=0;i--)
                System.out.print(" --> " + cidao[i]);
            sum=2*cidao[m-1]-now-cidao[0];//sum = (cidao[m-1] - now) + (cidao[m-1]-cidao[0])
        }
        //情况三：若当前磁道号大于请求序列中最小者且小于最大者
        if(now>cidao[0]&&now<cidao[m-1]){
            System.out.println("磁盘扫描序列为：");
            System.out.print(now);
            while(cidao[k]<now){
                k++;
            }
            left=k-1;
            right=k;
            for(j=left;j>=0;j--){
                System.out.print("-->"+cidao[j]);
            }
            for(j=m-1;j>left;j--){
                System.out.print("-->"+cidao[j]);
            }
            sum=2*cidao[m-1]-cidao[right]+now-2*cidao[0];//sum=(now-cidao[0])+(cidao[m-1]-cidao[0])+(cidao[m-1]-cidao[right])
        }
        ave = sum / (float) (m);
        System.out.println();
        System.out.println("移动总量为：" + sum);
        System.out.println("平均寻道长度为：" + ave);
    }
}